package db

import (
	"encoding/json"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"log"
	"strconv"
	"strings"
	"time"
)

func RedisToMysql() {
	nowTime := time.Now()

	// mysql 事务
	tx, err := db.Begin()
	if err != nil {
		log.Println("RedisToMysql error0,err = ", err)
		return
	}

	// redis 事务
	cx := cdkDb.pool.Get()
	err = cx.Send("MULTI")
	if err != nil {
		return
	}

	defer func() {
		// 执行事务
		tx.Commit()

		cx.Do("EXEC")
		cx.Close()
	}()

	// redis
	tables, err := cdkDb.Keys()
	if err != nil {
		log.Println("RedisToMysql error1,err = ", err)
		return
	}

	invalidCdkTypeIdMap := make(map[int]int) // 失效cdkTypeId map

	log.Println("step 0,", time.Since(nowTime))
	gameCdkTypeMap := ReadCdkTypeCache()
	//查询cdk 类时间是否过期
	for _, cdkTypeMap := range gameCdkTypeMap {
		for _, cdkType := range cdkTypeMap {
			endTime, err := time.ParseInLocation("2006-01-02 15:04:05", cdkType.EndDate, time.Local)
			if err != nil {
				log.Println("RedisToMysql error1.1,err = ", err)
				// 取消事务
				cx.Send("DISCARD")
				tx.Rollback()
				return
			}
			if nowTime.After(endTime) {
				// cdkType已经过期
				invalidCdkTypeIdMap[cdkType.Id] = 1
			}
		}
	}
	log.Println("step 1,", time.Since(nowTime))

	var cdkTableList []string    // 清单表名集合
	var cdkUseTableList []string // 使用表名集合
	// redis 表拆分成 清单表和使用表
	for _, tableName := range tables {
		if strings.Contains(tableName, "cdk_") {
			// 清单表
			cdkTableList = append(cdkTableList, tableName)
		} else if strings.Contains(tableName, "cdkuse_") {
			// 使用表
			cdkUseTableList = append(cdkUseTableList, tableName)

		}
	}
	log.Println("step 2,", time.Since(nowTime))

	// 清单表中删除cdkType已经过期的数据
	for _, listName := range cdkTableList {
		mysqlTableName := fmt.Sprintf("ulcdkey_cdk.%s", listName)

		// 清单表
		cdkList, err := cdkDb.HGETALL(listName)
		if err != nil {
			log.Println("RedisToMysql error2,err = ", err)
			// 取消事务
			cx.Send("DISCARD")
			tx.Rollback()
			return
		}

		for key, val := range cdkList {
			var data CdkInfo
			err = json.Unmarshal([]byte(val), &data)
			if err != nil {
				log.Println("RedisToMysql error3,err = ", err)
				// 取消事务
				cx.Send("DISCARD")
				tx.Rollback()
				return
			}

			//查询过期cdk类的 cdk 放入mysql
			_, ok := invalidCdkTypeIdMap[data.CdkTypeId]
			if ok {
				// 如果已经过了有效期,则转储到mysql
				data.Status = 2
				sqlStr := fmt.Sprintf(`insert into %s (cdkTypeId,cdkey,status,canUseTimes,bAll) values (%d,"%s",%d,%d,%d)`, mysqlTableName, data.CdkTypeId, key, data.Status, data.CanUseTimes, data.B_all)
				_, err = tx.Exec(sqlStr)
				if err != nil {
					log.Println("RedisToMysql error5,err = ", err)
					// 取消事务
					cx.Send("DISCARD")
					tx.Rollback()
					return
				}

				// 删除无效cdk清单数据
				_, err := redis.String(cx.Do("HDEL", listName, key))
				if err != nil {
					log.Println("RedisToMysql error12,err = ", err)
					// 取消事务
					cx.Send("DISCARD")
					tx.Rollback()
					return
				}
			}
		}
	}
	log.Println("step 3,", time.Since(nowTime))

	// 使用表 中 删除cdkType已经过期的数据
	for _, useListName := range cdkUseTableList {
		mysqlTableName := fmt.Sprintf("ulcdkey_cdkuse.%s", useListName)

		cdkUseList, err := cdkDb.HGETALL(useListName)
		if err != nil {
			log.Println("RedisToMysql error7,err = ", err)
			// 取消事务
			cx.Send("DISCARD")
			tx.Rollback()
			return
		}

		for bUserId, val := range cdkUseList {
			var data map[string]map[string]string
			err = json.Unmarshal([]byte(val), &data)
			if err != nil {
				log.Println("RedisToMysql error8,err = ", err)
				// 取消事务
				cx.Send("DISCARD")
				tx.Rollback()
				return
			}

			temp := make(map[string]map[string]string)
			for typeIdStr, cdkMap := range data {
				cdkTypeId, err := strconv.Atoi(typeIdStr)
				if err != nil {
					log.Println("RedisToMysql error9,err = ", err)
					// 取消事务
					cx.Send("DISCARD")
					tx.Rollback()
					return
				}
				_, ok := invalidCdkTypeIdMap[cdkTypeId]
				if ok {
					// 失效数据
					for cdk, bUseTime := range cdkMap {
						sqlStr := fmt.Sprintf(`insert into %s (bUserId,bUseTime,cdkey,cdkTypeId) values ("%s","%s","%s",%d)`, mysqlTableName, bUserId, bUseTime, cdk, cdkTypeId)
						_, err = tx.Exec(sqlStr)
						if err != nil {
							log.Println("RedisToMysql error10,err = ", err)
							// 取消事务
							cx.Send("DISCARD")
							tx.Rollback()
							return
						}
					}
				} else {
					// 组装成新的map 赋值回去
					temp[typeIdStr] = cdkMap
				}
			}

			if len(temp) == 0 {
				// 删除无效cdk使用数据后 user无cdk使用记录，则删除hash中该user数据
				_, err := redis.String(cx.Do("HDEL", useListName, bUserId))

				if err != nil {
					log.Println("RedisToMysql error11,err = ", err)
					// 取消事务
					cx.Send("DISCARD")
					tx.Rollback()
					return
				}
			} else {
				// 更新hash中user的cdk使用记录
				tempByte, err := json.Marshal(temp)
				if err != nil {
					log.Println("RedisToMysql error13,err = ", err)
					// 取消事务
					cx.Send("DISCARD")
					tx.Rollback()
					return
				}
				_, err = cx.Do("HSET", useListName, bUserId, string(tempByte))
				if err != nil {
					log.Println("RedisToMysql error14,err = ", err)
					// 取消事务
					cx.Send("DISCARD")
					tx.Rollback()
					return
				}
			}

		}
	}

	log.Println("step 4,", time.Since(nowTime))
	log.Println("redis转存mysql完成...")

	return
}
